
function low(v) {
  v = parseInt(v * 100 + 0.5)
  return v & 0xFF
}

function hig(v) {
  v = parseInt(v * 100 + 0.5)
  return (v >> 8) & 0xFF
}

function low_r(v) {
  return v & 0xFF
}

function hig_r(v) {
  return (v >> 8) & 0xFF
}

let fn = {
  mac: '',
  macReg: str => {
    return /(\w{2}:){5}\w{2}/.test(str)
  },
  macRepair: (mac, backupMac) => {
    if (!fn.macReg(mac)) {
      if (fn.macReg(backupMac)) {
        mac = backupMac
      } else {
        mac = '00:00:00:00:00:00'
      }
    }
    return mac
  },
  encryptStr({ cmdHead = 0x23, weight = 0, mac = '', resistance = 0, resistance500 = 0, left_weight = 0, heart_rate = 0 }) {
    // console.log('encryptStr', arguments)
    mac = fn.macRepair(mac, fn.mac)
    // console.log('mac', mac)
    let macArr = mac.split(':')
    let mac1 = macArr.slice(0, 3).map(item => +`0x${item}`).join(' ')
    let mac2 = macArr.slice(3).map(item => +`0x${item}`).join(' ')
    let w_low = low(weight)
    let w_hig = hig(weight)
    let r_low = low_r(resistance)
    let r_hig = hig_r(resistance)
    let r_500_low = low_r(resistance500)
    let r_500_hig = hig_r(resistance500)
    let l_w_low = low(left_weight)
    let l_w_hig = hig(left_weight)
    let d1 = parseInt(Math.random() * 99)
    let d2 = parseInt(Math.random() * 99)
    let d3 = parseInt(Math.random() * 99)
    let str = `${cmdHead} ${mac1} ${d1} ${mac2} ${w_hig} ${w_low} ${r_hig} ${r_low} ${d2} ${r_500_hig} ${r_500_low} ${l_w_low} ${l_w_hig} ${d3} ${heart_rate}`
    let cmdData = str.split(' ')
    let checksum = 0

    cmdData = cmdData.map((item) => {
      checksum += +item
      return +item
    })
    checksum = checksum & 0xFF
    cmdData.push(checksum)

    function odd(arr) {//奇数组
      return [arr[2], arr[0], arr[3], arr[1]]
    }

    function even(arr) {//偶数组
      return [arr[3], arr[0], arr[1], arr[2]]
    }

    let arr1 = odd(cmdData.slice(0, 4))
    let arr2 = even(cmdData.slice(4, 8))
    let arr3 = odd(cmdData.slice(8, 12))
    let arr4 = even(cmdData.slice(12, 16))
    let arr5 = odd(cmdData.slice(16, 20))

    let transtionArr1 = [0x43, 0x4A, 0x58, 0x4C]
    let transtionArr2 = [0x57, 0x46, 0x53, 0x4D]

    function transtion(arr, transtionArr) {
      return arr.map((t, i) => t ^ transtionArr[i])
    }

    function mergeArray(arr) {
      return (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]
    }

    function fomate2Btye(b) {
      let str = b.toString(16)
      while (str.length < 4) {
        str = '0' + str
      }
      return str
    }

    function move(tmp) {
      const t1 = (tmp << 17) & 0xFFFFFFFF
      const t2 = (tmp >> 15) & 0x1FFFF
      const result = (t1 | t2)
      const high = (result >> 16) & 0xFFFF
      const low = result & 0xFFFF

      return fomate2Btye(high) + fomate2Btye(low)
    }
    
    let newArr = []

    newArr[0] = transtion(arr1, transtionArr1)
    newArr[1] = transtion(arr2, transtionArr2)
    newArr[2] = transtion(arr3, transtionArr1)
    newArr[3] = transtion(arr4, transtionArr2)
    newArr[4] = transtion(arr5, transtionArr1)

    newArr[0] = mergeArray(newArr[0])
    newArr[1] = mergeArray(newArr[1])
    newArr[2] = mergeArray(newArr[2])
    newArr[3] = mergeArray(newArr[3])
    newArr[4] = mergeArray(newArr[4])

    newArr[0] = move(newArr[0])
    newArr[1] = move(newArr[1])
    newArr[2] = move(newArr[2])
    newArr[3] = move(newArr[3])
    newArr[4] = move(newArr[4])

    let tmpCmdData = cmdData.map(item => {
      let str = item.toString(16)
      return (str.length < 2 ? `0${str}` : str).toUpperCase()
    })

    return {
      originData: tmpCmdData.join(' '),
      str: newArr.join('').toUpperCase()
    }
  },
  buildCmd(cmd, deviceType, ...data) {
    const cmdData = [cmd, data.length + 4, deviceType]
    let checksum = 0
    cmdData.push(...data)
    cmdData.forEach((item) => checksum += item)
    checksum = checksum & 0xFF
    cmdData.push(checksum)

    return cmdData
  },
  accuracy(v1, v2) {//为防止小数计算，精度丢失
    let n = 10000

    return (v1 * n - v2 * n) / n
  },
  decodeImpedance(a1, b1) {
    return (a1 << 8) + b1
  },
  decodeWeight(a1, b1, weightScale = 100) {
    const d = (a1 << 8) + b1
    let weight = d / weightScale

    while (weight > 250) {
      weight = weight / 10
    }
    return weight
  },
  ishearRate(scaleType, _bit) {
    if (scaleType === 0x21) {
      return true
    } else if (scaleType === 0xff) {
      return (_bit >> 5) & 1
    }
    return false
  },
  arrayToHexString(data) {
    let str = ""

    data.forEach((item) => {
      const s = item.toString(16)
      if (s.length > 1) {
        str += s
      } else {
        str += '0' + s
      }
    })
    return str
  },
  getMacBroadcastScale(advertisArrayData) {
    let startNum = 2
    let endNum = 8
    let mac_temp = []

    for (let i = startNum; i < endNum; i++) {
      let item = advertisArrayData[i]
      let mac_str = item.toString(16).toLocaleUpperCase()
      if (mac_str.length === 1) {
        mac_str = `0${mac_str}`
      }
      mac_temp.push(mac_str)
    }

    return mac_temp.join(":")
  },
  getMac(advertisArrayData) {
    let startNum = 7
    let endNum = 13
    let mac_temp = []

    for (let i = startNum; i < endNum; i++) {
      let item = advertisArrayData[i]
      let mac_str = item.toString(16).toLocaleUpperCase()
      if (mac_str.length === 1) {
        mac_str = `0${mac_str}`
      }
      mac_temp.push(mac_str)
    }

    return mac_temp.reverse().join(":")
  },
  toHexArr(str) {
    let tmpArr = []
    for (let i = 0; i < str.length; i += 2) {
      tmpArr.push(+`0x${str[i]}${str[i + 1]}`)
    }

    return tmpArr
  },
  getCmdMac(cmdStr) {
    let str = cmdStr.slice(6, 18).toLocaleUpperCase()

    return fn.toHexArr2(str).reverse().join(':')
  },
  toHexArr2(str) {
    let tmpArr = []
    for (let i = 0; i < str.length; i += 2) {
      tmpArr.push(`${str[i]}${str[i + 1]}`)
    }

    return tmpArr
  },
}

let main = {
  itemCmdString(measure) {
    if (measure) {
      const {
        bodyfat,
        bmi
      } = measure

      const bodyfatItem = BodyfatReport.build(measure)
      const bmiItem = BmiReport.build(measure)

      const bodyfatLevel = bodyfatItem ? bodyfatItem.level + 1 : 1;
      const bmiLevel = bmiItem ? bmiItem.level + 1 : 1;

      const _bodyfat1 = (bodyfat * 10) >> 8
      const _bodyfat2 = (bodyfat * 10) & 0xFF

      const _bmi1 = (bmi * 10) >> 8
      const _bmi2 = (bmi * 10) & 0xFF

      return fn.buildCmd(0x1f, fn.scaleType, 0x10, _bodyfat1, _bodyfat2, bodyfatLevel, _bmi1, _bmi2, bmiLevel)
    }
    return ''
  },
  getDevice(device) {
    let broadcastTag = 'QN-S3'
    let { localName, advertisData } = device

    if (!advertisData) return null
    if (['QN-Scale', 'QN-Scale1', broadcastTag].indexOf(localName) == -1) return null
    if (device.RSSI === 127 || device.RSSI < -90) return null

    const advertisArrayData = fn.toHexArr(advertisData)
    const advertisDataString = advertisData

    if (!advertisArrayData || advertisDataString.length === 0) {
      return null
    }
    const hasScreenOn = advertisArrayData[5] !== 1
    if (!hasScreenOn) {
      //过滤没有开机的设备
      return null
    }
    let mac = ""
    let deviceInfo = {}

    if (localName == broadcastTag) {//广播秤
      let data = advertisArrayData
      let wl = data[9]
      let wh = data[10]
      let status = data[8]
      let weight = fn.decodeWeight(wh, wl)

      mac = fn.getMacBroadcastScale(advertisArrayData)
      deviceInfo.isBroadcast = true
      deviceInfo.weight = weight
      if ((status >> 0) & 1) {
        let resistance = fn.decodeImpedance(data[12], data[11])
        deviceInfo.code = 3003
        deviceInfo.encryptStr = fn.encryptStr({
          mac,
          weight,
          resistance: resistance,
          resistance500: resistance,
        }).str
      } else {
        deviceInfo.code = 3001
      }
    } else {
      mac = fn.getMac(advertisArrayData)
      deviceInfo.isBroadcast = false
    }

    fn.mac = mac

    deviceInfo.localName = localName
    deviceInfo.mac = mac
    deviceInfo.internal_model = advertisDataString.substring(4, 8).toUpperCase()
    deviceInfo.rssi = device.RSSI
    deviceInfo.deviceId = device.deviceId
    deviceInfo.advertisData = device.advertisData

    return deviceInfo
  },
  cmd({ device, value }) {
    if (!value) {
      return {
        code: '3004',
        msg: '缺少参数'
      }
    }
    let data = fn.toHexArr(value)
    const scaleType = data[2]
    fn.scaleType = scaleType
    let result = {}

    if (device) {
      switch (data[0]) {
        case 0x12:
          {
            if (fn.ishearRate(scaleType, data[10])) {
              fn.heartRate = true
            } else {
              fn.heartRate = false
            }
            const sendCmd = fn.buildCmd(0x13, scaleType, 0x01, 0x10, 0x00, 0x00, 0x00)

            result.code = 3000
            result.msg = ''
            result.cmdString = sendCmd
            result.serviceId = main.serviceId
            result.readCharacteristicId = main.readCharacteristicId
            result.writeCharacteristicId = main.writeCharacteristicId

            if (!fn.macReg(fn.mac)) {
              fn.mac = fn.getCmdMac(value)
              // console.log('getCmdMac', fn.getCmdMac(value))
            }
            break;
          }
        case 0x10:
          {
            const weight = fn.decodeWeight(data[3], data[4]);
            let resistance = fn.decodeImpedance(data[6], data[7])
            let resistance500 = fn.decodeImpedance(data[8], data[9])
            let heart_rate = 0

            if (fn.heartRate) {
              heart_rate = data[10] == 0 ? 0 : +("0x" + fn.arrayToHexString([data[10]]))
            }
            const scaleParam = {
              scale_weight: weight,
              resistance: resistance,
              resistance500: resistance500,
              heart_rate
            }
            if (scaleType == 0xff) {//心率双模秤
              let left_weight = fn.decodeWeight(data[11], data[12])

              Object.assign(scaleParam, {
                left_weight,
                right_weight: fn.accuracy(weight, left_weight)
              })
            }
            if (data[5] === 0) {
              fn.stableDataStatus = false
              result.code = 3001
              result.msg = '测量中'
              result.weight = weight
            } else if (data[5] === 1) {

              if (fn.heartRate) {
                let obj = {
                  mac: device.mac,
                  weight
                }
                Object.assign(obj, scaleParam)
                result.code = 3002
                result.msg = ''
                result.encryptStr = fn.encryptStr(obj).str
                result.serviceId = main.serviceId
                result.readCharacteristicId = main.readCharacteristicId
                result.writeCharacteristicId = main.writeCharacteristicId
              } else {
                //非心率秤，下发结束命令
                if (fn.stableDataStatus) return
                fn.stableDataStatus = true

                const sendCmd = fn.buildCmd(0x1f, scaleType, 0x10)
                result.code = 3003
                result.msg = '测量完成'
                let obj = {
                  mac: device.mac,
                  weight
                }
                Object.assign(obj, scaleParam)

                result.encryptStr = fn.encryptStr(obj).str
                result.cmdString = sendCmd
                result.serviceId = main.serviceId
                result.readCharacteristicId = main.readCharacteristicId
                result.writeCharacteristicId = main.writeCharacteristicId
              }
            } else if (data[5] === 2) {
              if (fn.stableDataStatus) return
              fn.stableDataStatus = true
              let obj = {
                mac: device.mac,
                weight
              }
              Object.assign(obj, scaleParam)
              result.code = 3003
              result.msg = '测量完成'
              result.encryptStr = fn.encryptStr(obj).str
              result.cmdString = fn.buildCmd(0x1f, scaleType, 0x10)
              result.serviceId = main.serviceId
              result.readCharacteristicId = main.readCharacteristicId
              result.writeCharacteristicId = main.writeCharacteristicId
            }
            break;
          }
      }
    } else {
      result = {
        code: 3004,
        msg: '缺少参数'
      }
    }

    return result
  },
  getServices(res) {
    let result = {
      serviceId: '0000FFE0-0000-1000-8000-00805F9B34FB',
      readCharacteristicId: '0000FFE1-0000-1000-8000-00805F9B34FB',
      writeCharacteristicId: '0000FFE3-0000-1000-8000-00805F9B34FB'
    }
    res.forEach((service) => {
      if (service.uuid === '0000FFF0-0000-1000-8000-00805F9B34FB') {
        result.serviceId = '0000FFF0-0000-1000-8000-00805F9B34FB'
        result.readCharacteristicId = '0000FFF1-0000-1000-8000-00805F9B34FB'
        result.writeCharacteristicId = '0000FFF2-0000-1000-8000-00805F9B34FB'
      }
    })
    main.serviceId = result.serviceId
    main.readCharacteristicId = result.readCharacteristicId
    main.writeCharacteristicId = result.writeCharacteristicId
    return result
  }
}

export default main